package com.erlang4j.api;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangDouble;
import com.ericsson.otp.erlang.OtpErlangInt;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.erlang4j.api.exceptions.Erlang4jUnexpectedParameterExpection;
import com.erlang4j.internal.messages.Binding;
import com.erlang4j.internal.messages.IMessageHandler;
import com.erlang4j.internal.stateMachine.IStateDefinition;
import com.erlang4j.internal.stateMachine.StateDefinition;

/**
 *<p>
 * This is a Domain Specific Language for manipulating erlang objects. Mostly this is used in tests, as the message
 * composer is usually a better way of composing non trivial erlang objects</p
 * <p>
 * Examples
 * <ul>
 * <li>x = list(atom("a"), string("b"), tuple(atom("c"))); <br>
 * &nbsp;&nbsp;&nbsp;represents [a,"b",{c}]
 * <li>x = make("a"); <br>
 * &nbsp;&nbsp;&nbsp;returns an OtpErlangString with value "a"
 * <li>x = make(1); <br>
 * &nbsp;&nbsp;&nbsp;returns an OtpErlangInteger with value 1 x = make(1.0); returns an OtpErlangDouble with value 1.0
 * </ul>
 * </p>
 * <p>
 * Design notes
 * <ul>
 * <li>This is intended to be used with import statics. If you dont like the import statics, you are probably better off
 * using new OtpString("asd") etc
 * <li>The binding methods uses Object... again this is designed to make the using code nicer looking
 * </p>
 * 
 * @author Phil Rice
 */
public class Erlang4jMessageLanguage {

	/**
	 * The parameters are in groups of two: first is the key, second is the value which is turned into an
	 * OtpErlangObject using make.
	 */
	public final static IBinding binding(Object... nameTypesAndValues) {
		IBinding result = new Binding();
		assert nameTypesAndValues.length % 2 == 0;
		for (int i = 0; i < nameTypesAndValues.length; i += 2) {
			String key = (String) nameTypesAndValues[i + 0];
			Object value = nameTypesAndValues[i + 1];
			assert value != null : "Key: " + key;
			OtpErlangObject object = make(i + 1, value);

			result = result.bind(key, object);
		}
		return result;
	}

	/** This is used with the {@link IErlang4j}.spawnStateMachine method. */
	public static IStateDefinition state(String stateName, IMessageHandler... handlers) {
		return new StateDefinition(stateName, handlers);
	}

	/** This turns the parameters into an array of OtpErlangObjects,using the make method. */
	public static OtpErlangObject[] makeArray(Object... objects) {
		OtpErlangObject[] result = new OtpErlangObject[objects.length];
		for (int i = 0; i < objects.length; i++) {
			result[i] = make(i, objects[i]);
		}
		return result;
	}

	/**
	 * This return the parameter if its already an OtpErlangObject, turns strings into OtpErlangStrings, integers into
	 * OrpErlangIntegers and floats in OptErlangFloats
	 */
	public static OtpErlangObject make(Object object) {
		return make(0, object);
	}

	/**
	 * This return the parameter if its already an OtpErlangObject, turns strings into OtpErlangStrings, integers into
	 * OrpErlangIntegers and floats in OptErlangFloats. The parameter i is only needed for the error message
	 */
	public static OtpErlangObject make(int i, Object object) {
		assert object != null;
		if (object instanceof OtpErlangObject)
			return (OtpErlangObject) object;
		if (object instanceof String)
			return new OtpErlangString((String) object);
		if (object instanceof Integer)
			return new OtpErlangInt((Integer) object);
		else if (object instanceof Float)
			return new OtpErlangDouble(((Float) object));
		else if (object instanceof Double)
			return new OtpErlangDouble(((Double) object));
		else
			throw new Erlang4jUnexpectedParameterExpection(i, object);
	}

	/** return an OtpErlangAtom with a value equal to the parameter */
	public static OtpErlangAtom atom(String string) {
		return new OtpErlangAtom(string);
	}

	/** return an OtpErlangString with a value equal to the parameter */
	public static OtpErlangString string(String string) {
		return new OtpErlangString(string);
	}

	/** return an OtpErlangInt with a value equal to the parameter */
	public static OtpErlangInt integer(int i) {
		return new OtpErlangInt(i);
	}

	/** return an OtpErlangDouble with a value equal to the parameter */
	public static OtpErlangDouble floating(double d) {
		return new OtpErlangDouble(d);
	}

	/**
	 * return an OtpErlangTuple with a value equal to the parameters that have been turned into OtpErlangObjects using
	 * makeArray
	 */
	public static OtpErlangTuple tuple(Object... objects) {
		return new OtpErlangTuple(makeArray(objects));
	}

	/**
	 * return an OtpErlangList with a value equal to the parameters that have been turned into OtpErlangObjects using
	 * makeArray
	 */
	public static OtpErlangList list(Object... objects) {
		return new OtpErlangList(makeArray(objects));
	}

}
